/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package rekeningrijdenserver;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.security.PublicKey;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.crypto.SecretKey;
import tijdserver.TijdServer;
import util.Certificate;
import util.Message;
import util.Message.MessageType;
import util.NetwerkComponent;
import util.SecurityManager;
import util.Sniffer;

/**
 *
 * @author Jonas
 */
public class RekeningRijdenServerImpl implements RekeningRijdenServer {

    private static int IDCounter = 0;
    private String ID;
    private Sniffer sniffy;
    private TijdServer ts;
    private NetwerkComponent requestor;
    private SecurityManager sm;
    private SecretKey secret;
    HashMap<String, Object> reply = new HashMap<String, Object>();

    public RekeningRijdenServerImpl(TijdServer ts) {
        ID = "RRS-id" + IDCounter;
        IDCounter++;
        this.ts = ts;
        this.sm = new SecurityManager(null);
    }

    public void sendMessage(Message msg, NetwerkComponent sender) {
        if (sniffy != null) {
            sniffy.readTransmission(msg, sender, this);
        }
        if (msg.getMessageType() == MessageType.CLIENT_HELLO) {
            // analyse ciphersuite
            // ... we just work with predefined ciphers ...

            // send serverHello
            HashMap<String, Object> serverHello = new HashMap<String, Object>();
            serverHello.put("response", "ok");
            sender.sendMessage(new Message(MessageType.SERVER_HELLO, serverHello), this);

            // send cert
            HashMap<String, Object> cert = new HashMap<String, Object>();
            try {
                cert.put("certificate", SecurityManager.generateCertificate("./RRSPublic.key"));
            } catch (IOException ex) {
            }
            sender.sendMessage(new Message(MessageType.SERVER_CERT, cert), this);
        }
        if (msg.getMessageType() == MessageType.CLIENT_CERT) {
            //obtain client's public key
            Certificate cert = (Certificate) msg.getData().get("certificate");
            //this.sm.otherPublicKey = cert.getPublicKey();
            // now wait for client_Key_exchange;
        }
        if (msg.getMessageType() == MessageType.CLIENT_KEY_EXCHANGE) {
            byte[] salt = (byte[]) msg.getData().get("salt");
            char[] pre_master_secret = (char[]) msg.getData().get("pre_master_secret");
            secret = SecurityManager.getSecretKey(pre_master_secret, salt);
            //this.sm.setSecretKey(pre_master_secret, salt);

            Message ok = new Message(MessageType.CONN_OK, new HashMap<String, Object>());
            try {
                requestor.sendMessage(SecurityManager.encryptMessage(SecurityManager.readPublicKeyFromFile("./bestuurderPublic.key"), ok), this);
                } catch (Exception ex) {
                Logger.getLogger(RekeningRijdenServerImpl.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
        if (msg.getMessageType() == MessageType.RRS_REQ) {
            // check integrity
            ArrayList<Object> signed = new ArrayList<Object>();
            signed.add(msg.getData().get("TBegin"));
            signed.add(msg.getData().get("TEnd"));
            signed.add(msg.getData().get("FCat"));
            boolean signatureOK = SecurityManager.verifyData(signed, "./bestuurderPublic.key", (byte[]) msg.getData().get("Signature"));

            signed.clear();
            signed.add(msg.getData().get("Signature"));
            signed.add(msg.getData().get("Time"));
            boolean signTsOK = SecurityManager.verifyData(signed, "./TSPublic.key", (byte[]) msg.getData().get("SignTS"));

            if (signTsOK && signatureOK) {
                // process message
                int[] cats = (int[]) msg.getData().get("FCat");
                long begin = (Long) msg.getData().get("TBegin");
                long end = (Long) msg.getData().get("TEnd");

                try {
                    PrintWriter out = new PrintWriter(new FileWriter("serverData.txt", true));
                    out.println("From " + sender);
                    out.println("TBegin: " + new Date(begin));
                    out.println("TEnd: " + new Date(end));
                    out.println("#DUUR: " + cats[0]);
                    out.println("#STANDAARD: " + cats[1]);
                    out.println("#GOEDKOOP: " + cats[2]);
                    out.println("=====");
                    out.close();
                } catch (IOException ex) {
                }

                // send reply
                reply.put("TBegin", begin);
                reply.put("TEnd", end);

                ArrayList<Object> original = new ArrayList<Object>();
                original.add(begin);
                original.add(end);
                original.add(cats);
                original.add(msg.getData().get("SignBestuurder"));
                original.add(msg.getData().get("Time"));
                original.add(msg.getData().get("SignTS"));

                byte[] signature = SecurityManager.signData(original, "./RRSPrivate.key");
                reply.put("Signature", signature);

                HashMap<String, Object> hash = new HashMap<String, Object>();
                hash.put("Signature", signature);
                Message transmit = new Message(Message.MessageType.TS_REQ, hash);
                ts.sendMessage(transmit, this);
            }
        }
        if (msg.getMessageType() == MessageType.TS_ANSWER) {
            byte[] data = (byte[]) reply.get("Signature");
            Long time = (Long) msg.getData().get("Timestamp");
            byte[] signature = (byte[]) msg.getData().get("SignTS");

            ArrayList<Object> signed = new ArrayList<Object>();
            signed.add(data);
            signed.add(time);

            boolean ok = SecurityManager.verifyData(signed, "./TSPublic.key", signature);
            if (ok) {
                reply.put("Time", time);
                reply.put("SignTS", signature);
                Message toSend = new Message(MessageType.RRS_ANSWER, reply);
                Message enc;
                try {
                    enc = SecurityManager.encryptMessage(secret, toSend);
                    requestor.sendMessage(enc, this);
                } catch (Exception ex) {}
            }
        }
        if (msg.getMessageType() == MessageType.ENCRYPTED) {
            requestor = sender;
            try {
                Message decrypted = SecurityManager.decryptMessage(secret, msg);
                sendMessage(decrypted, this);
            } catch (Exception ex) {
                Logger.getLogger(RekeningRijdenServerImpl.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
        if (msg.getMessageType() == MessageType.PRE_ENCRYPTED) {
            requestor = sender;
            try {
                Message decrypted = SecurityManager.decryptMessage(SecurityManager.readPrivateKeyFromFile("./RRSPrivate.key"), msg);
                sendMessage(decrypted, this);
            } catch (Exception ex) {
                Logger.getLogger(RekeningRijdenServerImpl.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
        /*if(msg.getMessageType() == MessageType.CLIENT_HELLO){
        sm.boordComputer = (BoordComputer) sender;
        // interpret message & set cipher suite
        sm.setCypherSuite("");
        // send back server hello
        sm.serverHello();
        }*/
    }

    @Override
    public String toString() {
        return ID;
    }

    // keypair generation
    /*private void GenerateOwnKeys(){

    try{

    // Generate Assymetric keypair via RSA with 2048 bits
    KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
    kpg.initialize(2048);
    KeyPair kp = kpg.genKeyPair();

    // store keys
    publicServerKey = kp.getPublic();
    privateServerKey = kp.getPrivate();

    } catch (NoSuchAlgorithmException e) {
    }
    }

    public PublicKey Ping() {
    // returns Server PublicKey
    return this.publicServerKey;
    }*/
    public PublicKey Ping() {
        System.out.println("Not used");
        return null;
    }

    public void addSniffer(Sniffer sniffer) {
        sniffy = sniffer;
        System.out.println("Sniffer added");
    }

    public void removeSniffer() {
        sniffy = null;
        System.out.println("Sniffer removed");
    }

    public int[] getCategoriesPhysical(){
        int[] cats = new int[]{0,0,0};
        try{
            BufferedReader in = new BufferedReader(new FileReader("serverData.txt"));
            String str;
            while ((str = in.readLine()) != null) {
                if(str.contains("#DUUR:")){
                    cats[0] += Integer.parseInt(str.substring(7));
                }
                if(str.contains("#STANDAARD:")){
                    cats[1] += Integer.parseInt(str.substring(12));
                }
                if(str.contains("#GOEDKOOP:")){
                    cats[2] += Integer.parseInt(str.substring(11));
                }
            }
            in.close();
        }catch(IOException ioe){}
        return cats;
    }
}
